﻿#pragma once

#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRep_Tool.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <GeomLib_Tool.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_BSplineSurface.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColgp_HArray1OfPnt.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <vector>

#include <occmesh/api/step.h>

struct Bounding
{
  private:
    double m_xmin, m_ymin, m_zmin;
    double m_xmax, m_ymax, m_zmax;

  public:
    void Init();
    void Update(const gp_Pnt &p);
    bool Overlap(const Bounding &rhs);
};

struct HalfParamData
{
    static constexpr double overlapThreshold{1};  // 公共边重合检测阈值
    static constexpr int overlapSample{10};       // 检测公共边是否重叠时的采样数
    static constexpr int fittingEdgeSample{2000}; // 拟合裁剪线框时的采样数
    static constexpr int commonEdgeSample{6};     // 捏合线框时的采样数
};

struct Vertex;
struct Edge;
struct HalfEdge;
struct Wire;

struct HalfEdge
{
    TopoDS_Edge occEdge; // 保存几何边
    TopoDS_Face occFace; // 保存生成的面

    // 拓扑连接
    HalfEdge *next{nullptr};
    HalfEdge *prev{nullptr};
    HalfEdge *twin{nullptr};
    Edge *edge{nullptr};

  private:
    Bounding m_bounding;
    gp_Pnt m_startPoint, m_endPoint;
    gp_Vec m_startPerpTangent, m_endPerpTangent; // 起点、终点切向场，用于沿边界插值
    bool m_sameDirectionWithTwin;                // true 表示公共边同向，false 表示公共边反向
    Step m_perpDirOfPoles;                       // 垂直于公共边的方向
    std::vector<Step> m_indexRangeOfPoles;       // 公共边控制点的范围

  public:
    HalfEdge(TopoDS_Edge occEdge);
    bool SameDirectionWithEdge() const;
    bool SameDirectionWithTwin() const;
    void AccumulateVertex();                     // 累计顶点坐标，只有在 edge 构建完成后可以调用
    void AccumulateNormal();                     // 累计顶点法向
    void BuildPerpTangent();                     // 计算切向场切向
    Handle(Geom_BSplineSurface) Surface() const; // 获取公共边所在的曲面
    gp_Pnt StartPoint() const;
    gp_Pnt EndPoint() const;
    gp_Vec StartPerpTangent() const;
    gp_Vec EndPerpTangent() const;
    bool Overlap(HalfEdge *rhs); // 检测半边是否重叠，如果重叠，记录它们的方向关系
};

struct Edge
{
    HalfEdge *halfEdge;
    Vertex *startVertex{nullptr}; // 起点，与 half->startPoint 相同
    Vertex *endVertex{nullptr};   // 终点，与 half->endPoint 相同

    Edge(HalfEdge *halfEdge);
    void ConnectEdge(Edge *rhs); // 检查几何顶点，如果存在重合，将当前边的拓扑顶点赋予 rhs
};

struct Vertex
{
  private:
    gp_Pnt m_position{0, 0, 0};
    gp_Vec m_normal{0, 0, 0};
    int m_count{0};              // 记录累计顶点数
    std::vector<Edge *> m_edges; // 记录与该顶点相连的边

  public:
    Vertex(Edge *edge);
    void Accumulate(const gp_Pnt &p);
    void Accumulate(const gp_Vec &n);
    gp_Pnt Point() const;
    gp_Vec Normal() const;
    bool IsBoundary() const; // 判断是否是边界顶点
    bool IsNearVoid() const; // 判断周围是否有未填充的洞
    void FindEdges();        // 查找与该顶点相连的边，如果不是内部顶点，则查找失败
    const std::vector<Edge *> &Edges() const;
};

struct Wire
{
    HalfEdge *halfEdge;

    Wire(HalfEdge *halfEdge);
    void SetFace(TopoDS_Face face);
    TopoDS_Face Face() const;
    std::vector<TopoDS_Face> Faces() const; // 获得相邻的所有面
    std::vector<TopoDS_Edge> Edges() const; // 获得所有边
    void RebuildWire();                     // 重新生成 wire，将重叠边、重合顶点合并
};